<!---
Copyright (c) 2007 Brian Billman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--->
<cfcomponent displayname="CFSmugImages" extends="CFSmugBase" output="false">

	<cffunction name="get" access="public" output="false" returntype="array" hint="This method will fetch all the ImageIDs for the album specified by AlbumID. The Album must be owned by the Session holder, or else be Public (if password-protected, a Password must be provided), to return results. Otherwise, an 'invalid user' faultCode will result.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="AlbumID" type="numeric" required="true" />
		<cfargument name="Heavy" type="boolean" required="false" default="false" hint="Different results returned based on SessionID" />
		<cfargument name="Password" type="string" required="false" />
		<cfargument name="SitePassword" type="string" required="false" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />
		<cfset var passwordExists = false />
		<cfset var result = 0 />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.get ), argsPassed = Arguments ) />

		<cfif StructKeyExists( Arguments, "Password" ) Or StructKeyExists( Arguments, "SitePassword" )>
			<cfset passwordExists = true />
		</cfif>

		<cfset response = doRemoteInvocation( method = "smugmug.images.get",
											  args = argsCopy,
											  secureMode = passwordExists )  />

		<cftry>
			<cfset result = convertResponse( response ) />

			<cfcatch type="CFSmug.SmugMugException">
				<!--- if "empty set - no images found", return empty array --->
				<cfif cfcatch.errorcode eq getErrorCodes().EMPTY_SET>
					<cfset result = ArrayNew( 1 ) />
				<cfelse>
					<cfrethrow />
				</cfif>
			</cfcatch>
		</cftry>

		<cfreturn result />
	</cffunction>

	<cffunction name="getEXIF" access="public" output="false" returntype="struct" hint="This method will return camera and photograph details about the image specified by ImageID. The Album must be owned by the Session holder, or else be Public (if password-protected, a Password must be provided), to return results. Otherwise, an 'invalid user' faultCode will result. Additionally, the album owner must have specified that EXIF data is allowed. Note that many photos have no EXIF data, so an empty or partially returned result is very normal.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />
		<cfargument name="Password" type="string" required="false" />
		<cfargument name="SitePassword" type="string" required="false" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />
		<cfset var passwordExists = false />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.getEXIF ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.images.getEXIF",
											  args = argsCopy,
											  secureMode = passwordExists )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="getURLs" access="public" output="false" returntype="struct" hint="This method will return all the URLs for the various sizes of the image specified by ImageID. The Album must be owned by the Session holder, or else be Public (if password-protected, a Password must be provided), to return results. Otherwise, an 'invalid user' faultCode will result. Additionally, obvious restrictions on Originals and Larges apply if so set by the owner. They will return as empty strings for those URLs if they're unavailable.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />
		<cfargument name="TemplateID" type="numeric" required="false" default="3" hint="specifies which Style to build the AlbumURL with. Default: 3. Possible values: Elegant: 3; Traditional: 4; All Thumbs: 7; Slideshow: 8; Journal: 9" />
		<cfargument name="Password" type="string" required="false" />
		<cfargument name="SitePassword" type="string" required="false" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />
		<cfset var passwordExists = false />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.getURLs ), argsPassed = Arguments ) />

		<cfif StructKeyExists( Arguments, "Password" ) Or StructKeyExists( Arguments, "SitePassword" )>
			<cfset passwordExists = true />
		</cfif>

		<cfset response = doRemoteInvocation( method = "smugmug.images.getURLs",
											  args = argsCopy,
											  secureMode = passwordExists )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="getInfo" access="public" output="false" returntype="struct" hint="This method will return details about the image specified by ImageID. The Album must be owned by the Session holder, or else be Public (if password-protected, a Password must be provided), to return results.. Otherwise, an 'invalid user' faultCode will result. Additionally, some fields are only returned to the Album owner.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />
		<cfargument name="Password" type="string" required="false" />
		<cfargument name="SitePassword" type="string" required="false" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />
		<cfset var passwordExists = false />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.getInfo ), argsPassed = Arguments ) />

		<cfif StructKeyExists( Arguments, "Password" ) Or StructKeyExists( Arguments, "SitePassword" )>
			<cfset passwordExists = true />
		</cfif>

		<cfset response = doRemoteInvocation( method = "smugmug.images.getInfo",
											  args = argsCopy,
											  secureMode = passwordExists )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="changeSettings" access="public" output="false" returntype="boolean" hint="This method updates specific settings for a given image specified by ImageID. If any of the parameters are missing or are empty strings '', they're ignored and the current settings are preserved. Otherwise, they're updated with the new settings. This method can be used to update Captions, Keywords, or to move an image from one Album to another.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />
		<cfargument name="AlbumID" type="numeric" required="false" hint="AlbumID of where to move image to (if passed)" />
		<cfargument name="Caption" type="string" required="false" />
		<cfargument name="Keywords" type="string" required="false" hint="Keywords should be separated by a semi-colon." />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.changeSettings ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.images.changeSettings",
											  args = argsCopy )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="changePosition" access="public" output="false" returntype="boolean" hint="This method changes the Position of an image within it's Album.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />
		<cfargument name="Position" type="numeric" required="true" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.changePosition ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.images.changePosition",
											  args = argsCopy )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="upload" access="public" output="false" returntype="numeric" hint="This method will upload a file to the specified Album (identified by AlbumID) OR replace the given ImageID with the required parameters. Returns the new ImageID.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="AlbumID" type="numeric" required="false" hint="The AlbumID you're adding the photo to" />
		<cfargument name="ImageID" type="numeric" required="false" hint="The ImageID you're replacing with this photo" />
		<cfargument name="FileName" type="string" required="true" />
		<cfargument name="Data" type="binary" required="true" />
		<cfargument name="ByteCount" type="numeric" required="false" />
		<cfargument name="MD5Sum" type="string" required="false" />
		<cfargument name="Caption" type="string" required="false" hint="Sets the Caption on the image. For multi-line captions, use a carriage return between lines" />
		<cfargument name="Keywords" type="string" required="false" hint="Keywords should be separated by a semicolon." />
		<cfargument name="Latitude" type="string" required="false" hint="Latitude of the image in the form D.d, such as 37.430096" />
		<cfargument name="Longitude" type="string" required="false" hint="Longitude of the image in the form D.d, such as -122.152269" />
		<cfargument name="Altitude" type="string" required="false" hint="Altitude of the image in meters" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfif Not StructKeyExists( Arguments, "AlbumID" ) And Not StructKeyExists( Arguments, "ImageID" )>
			<cfset logAction( file="CFSmug.CFSmugImages.upload", text="Invalid Arguments; Detail = AlbumID or ImageID is required.  If both are passed, ImageID replaced with new image.", type="Warning" ) />
			<cfthrow type="CFSmug.CFSmugImages.upload" message="Invalid Arguments" detail="AlbumID or ImageID is required.  If both are passed, ImageID replaced with new image." />
		</cfif>

		<cfparam name="Arguments.ByteCount" type="numeric" default="#Len( Arguments.Data )#" />
		<cfparam name="Arguments.MD5Sum" type="string" default="#LCase( Hash( ToString( Arguments.Data, getConfig().getPageEncoding() ) ) )#" />

		<!---
			Content-Length
				required
				standard HTTP header, must match the actual bytes sent in the body (your image)
			Content-MD5
				required
				standard HTTP header, must be an MD5 of the data in the body (your image)
			X-Smug-SessionID
				required
				SessionID from API login calls
			X-Smug-Version
				required
				API Version (ex: 1.1.1)
			X-Smug-ResponseType
				required
				set "XML-RPC", "REST", "JSON" or "PHP" depending on your desired response formatting.
			X-Smug-AlbumID
				required (for uploading new photos, not for replacing existing photos)
				The AlbumID you're adding the photo to
			X-Smug-ImageID
				required (for replacing existing photos, not for uploading new photos)
				The ImageID of the photo you are replacing
			X-Smug-FileName
				optional
				The filename of the photo you're adding
			X-Smug-Caption
				optional
				Sets the Caption on the image. For multi-line captions, use a carriage return between lines
			X-Smug-Keywords
				optional
				Sets the Keywords on the image
			X-Smug-Latitude
				optional
				Sets the Latitude of the image (in the form D.d, such as 37.430096)
			X-Smug-Longitude
				optional
				Sets the Longitude of the image (in the form D.d, such as -122.152269)
			X-Smug-Altitude
				optional
				Sets the Altitude of the image (in meters)
		 --->

		<cfif StructKeyExists( Arguments, "SessionID" )>
			<cfset argsCopy["X-Smug-SessionID"] = Arguments.SessionID />
		</cfif>

		<cfif StructKeyExists( Arguments, "AlbumID" )>
			<cfset argsCopy["X-Smug-AlbumID"] = Arguments.AlbumID />
		</cfif>

		<cfif StructKeyExists( Arguments, "ImageID" )>
			<cfset argsCopy["X-Smug-ImageID"] = Arguments.ImageID />
		</cfif>

		<cfif StructKeyExists( Arguments, "FileName" )>
			<cfset argsCopy["X-Smug-FileName"] = Arguments.FileName />
		</cfif>

		<cfif StructKeyExists( Arguments, "Data" )>
			<cfset argsCopy["Data"] = Arguments.Data />
		</cfif>

		<cfif StructKeyExists( Arguments, "ByteCount" )>
			<cfset argsCopy["Content-Length"] = Arguments.ByteCount />
		</cfif>

		<cfif StructKeyExists( Arguments, "MD5Sum" )>
			<cfset argsCopy["Content-MD5"] = Arguments.MD5Sum />
		</cfif>

		<cfif StructKeyExists( Arguments, "Caption" )>
			<cfset argsCopy["X-Smug-Caption"] = Arguments.Caption />
		</cfif>

		<cfif StructKeyExists( Arguments, "Keywords" )>
			<cfset argsCopy["X-Smug-Keywords"] = Arguments.Keywords />
		</cfif>

		<cfif StructKeyExists( Arguments, "Latitude" )>
			<cfset argsCopy["X-Smug-Latitude"] = Arguments.Latitude />
		</cfif>

		<cfif StructKeyExists( Arguments, "Longitude" )>
			<cfset argsCopy["X-Smug-Longitude"] = Arguments.Longitude />
		</cfif>

		<cfif StructKeyExists( Arguments, "Altitude" )>
			<cfset argsCopy["X-Smug-Altitude"] = Arguments.Altitude />
		</cfif>

		<cfset argsCopy["X-Smug-Version"] = getAPIVersion() />
		<cfset argsCopy["X-Smug-ResponseType"] = getResponseType() />

		<cfset response = doRemoteInvocation( method = "smugmug.images.upload",
											  args = argsCopy,
										  	  upload = true )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="uploadFromURL" access="public" output="false" returntype="numeric" hint="This method will upload a file from a specified URL to the specified Album (identified by AlbumID) with the required parameters. Returns the new ImageID.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="AlbumID" type="numeric" required="true" />
		<cfargument name="URL" type="string" required="true" />
		<cfargument name="ByteCount" type="numeric" required="false" />
		<cfargument name="MD5Sum" type="string" required="false" />
		<cfargument name="Caption" type="string" required="false" hint="Sets the Caption on the image. For multi-line captions, use a carriage return between lines" />
		<cfargument name="Keywords" type="string" required="false" hint="Keywords should be separated by a semi-colon." />
		<cfargument name="Latitude" type="string" required="false" hint="Latitude of the image in the form D.d, such as 37.430096" />
		<cfargument name="Longitude" type="string" required="false" hint="Longitude of the image in the form D.d, such as -122.152269" />
		<cfargument name="Altitude" type="string" required="false" hint="Altitude of the image in meters" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.uploadFromURL ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.images.uploadFromURL",
											  args = argsCopy )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="delete" access="public" output="false" returntype="void" hint="This method deletes the specified Image.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.delete ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.images.delete",
											  args = argsCopy )  />

		<cfset result = convertResponse( response ) />

		<cfreturn />
	</cffunction>

	<cffunction name="getStats" access="public" output="false" returntype="struct" hint="Gets transfer statitistics for the given Image during the given Month. SmugMug often only keeps the current month and the one previous, but you never know. A float is provided for Originals because videos often are only partially viewed.">
		<cfargument name="SessionID" type="string" required="true" />
		<cfargument name="ImageID" type="numeric" required="true" />
		<cfargument name="Month" type="numeric" required="true" />

		<cfset var response = 0 />
		<cfset var argsCopy = structNew() />

		<cfset argsCopy = createSmugMugArgs( functionMetadata = getMetaData( this.getStats ), argsPassed = Arguments ) />

		<cfset response = doRemoteInvocation( method = "smugmug.images.getStats",
											  args = argsCopy )  />

		<cfreturn convertResponse( response ) />
	</cffunction>

	<cffunction name="convertResponse" access="private" output="false" returntype="any">
		<cfargument name="responseXML" type="xml" required="true" />
		<cfset var x = 0 />
		<cfset var s = 0 />
		<cfset var c1 = 0 />

		<cftry>

			<cfif validateRESTResponse( Arguments.responseXML )>
				<cfset x = Arguments.responseXML.rsp />
			</cfif>

			<cfswitch expression="#LCase( x.method.XmlText )#">

				<cfcase value="smugmug.images.get">
					<!---
						Array of ImageIDs
					--->
					<cfset s = ArrayNew(1) />
					<cfloop from="1" to="#ArrayLen( x.Images.Image )#" index="c1">
						<cfset s[c1] = StructNew() />
						<cfset s[c1] = createImageStruct( ImageNode = x.Images.Image[c1] ) />
					</cfloop>
				</cfcase>

				<cfcase value="smugmug.images.getexif;smugmug.images.geturls;smugmug.images.getinfo;smugmug.images.getstats" delimiters=";">
					<!---
					smugmug.images.getexif
						struct "Image" [some, none, or all may be returned]
							int "id"
							String "DateTime"
							String "DateTimeOriginal"
							String "DateTimeDigitized"
							String "Make"
							String "Model"
							String "ExposureTime"
							String "Aperture"
							int "ISO"
							String "FocalLength"
							String "FocalLengthIn35mmFilm"
							String "CCDWidth"
							String "CompressedBitsPerPixel"
							int "Flash"
							int "Metering"
							int "ExposureProgram"
							String "ExposureBiasValue"
							int "ExposureMode"
							int "LightSource"
							int "WhiteBalance"
							String "DigitalZoomRatio"
							int "Contrast"
							int "Saturation"
							int "Sharpness"
							String "SubjectDistance"
							String "SubjectDistanceRange"
							int "SensingMethod"
							String "ColorSpace"
							String "Brightness"

					smugmug.images.geturls
						struct
							String "AlbumURL"
							String "TinyURL"
							String "ThumbURL"
							String "SmallURL"
							String "MediumURL"
							String "LargeURL" (if available)
							String "XLargeURL" (if available)
							String "X2LargeURL" (if available)
							String "X3LargeURL" (if available)
							String "OriginalURL" (if available)

					smugmug.images.getinfo
						struct "Image"
							int "id"
							String "Caption"
							int "Position"
							int "Serial"
							int "Size"
							int "Width"
							int "Height"
							String "LastUpdated"
							String "FileName" owner only
							String "MD5Sum" owner only
							String "Watermark" owner only
							String "Format"  owner only
							String "Keywords"
							String "Date" owner only
							String "AlbumURL"
							String "TinyURL"
							String "ThumbURL"
							String "SmallURL"
							String "MediumURL"
							String "LargeURL" (if available)
							String "XLargeURL" (if available)
							String "X2LargeURL" (if available)
							String "X3LargeURL" (if available)
							String "OriginalURL" (if available)
							struct "Album"
								id

					smugmug.images.getstats
						struct "Image"
							int id
							int Bytes
							int Tiny
							int Thumb
							int Small
							int Medium
							int Large
							float Original

					--->

					<cfset s = StructNew() />
					<cfset s = createImageStruct( ImageNode = x.Image ) />
				</cfcase>

				<cfcase value="smugmug.images.changesettings;smugmug.images.changeposition" delimiters=";">
					<!---
						boolean
					--->
					<cfif StructKeyExists( x, "Image" )>
						<cfset s = true />
					<cfelse>
						<cfset s = false />
					</cfif>
				</cfcase>

				<cfcase value="smugmug.images.upload;smugmug.images.replace" delimiters=";">
					<!---
						int "ImageID"
					--->
					<cfset s = x.ImageID.XmlText />
				</cfcase>

				<cfcase value="smugmug.images.uploadfromurl">
					<!---
						int "ImageID"
					--->
					<cfset s = x.Image.XmlAttributes.id />
				</cfcase>

				<cfcase value="smugmug.images.delete">
					<!---
						String == "SUCCESS"
					--->

					<cfset s = "SUCCESS" />
				</cfcase>

				<cfdefaultcase>
					<cfset logAction( file="CFSmug.CFSmugImages.convertResponse", text="Message = Received response from unexpected method: '#x.method.XmlText#'", type="Warning" ) />
					<cfthrow type="CFSmug.CFSmugImages.convertResponse" message="Received response from unexpected method: '#x.method.XmlText#'" />
				</cfdefaultcase>

			</cfswitch>

			<cfreturn s />


			<cfcatch type="CFSmug">
				<cfrethrow />
			</cfcatch>
			<cfcatch type="any">
				<cfset logAction( file="CFSmug.CFSmugImages.convertResponse", text="Error converting response; Message=#cfcatch.message#; Detail=#cfcatch.detail#; Stacktrace=#cfcatch.stacktrace#", type="Error" ) />
				<cfthrow type="CFSmug.CFSmugImages.convertResponse"
						message="Error converting response" detail="Message: #cfcatch.message#; Detail: #cfcatch.detail#" />
			</cfcatch>
		</cftry>
	</cffunction>

</cfcomponent>